﻿using System;

namespace PublicDomain.Data.Csv
{
    public static class DelimitedString
    {
        public static string[] Split(this string delimitedText, char delimiter, bool escapedEmptyAsZeroLengthString)
        {
            var result = new string[0];

            if (String.IsNullOrEmpty(delimitedText))
            {
                return result;
            }

            int i = 0;
            int fieldStartIndex = 0;
            while (fieldStartIndex < delimitedText.Length)
            {
                var fieldText = ParseField(fieldStartIndex, delimitedText, delimiter, escapedEmptyAsZeroLengthString, out fieldStartIndex);

                var newLength = result.Length + 1;
                Array.Resize<string>(ref result, newLength);

                result[i] = fieldText;

                i++;
            }

            return result;
        }

        public static string RemoveHexLiteralPrefix(this string text)
        {
            var hexLiteralPrefixes = new string[] { "\\x", "\\X", "0x", "0X", "x", "X", "&h", "&H", "\\u", "\\U" };

            foreach (var hexLiteralPrefix in hexLiteralPrefixes)
            {
                string removedPrefixText;

                if (TryRemovePrefix(text, hexLiteralPrefix, out removedPrefixText))
                {
                    return removedPrefixText;
                }
            }

            return text;
        }

        public static bool TryRemovePrefix(this string text, string prefix, out string removedPrefixText)
        {
            if (String.IsNullOrEmpty(text))
            {
                removedPrefixText = text;
                return false;
            }

            if (String.IsNullOrEmpty(prefix))
            {
                removedPrefixText = text;
                return false;
            }

            if (text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
            {
                removedPrefixText = text.Remove(0, prefix.Length);
                return true;
            }

            removedPrefixText = text;
            return false;
        }

        private static string ParseField(int fieldStartIndex, string delimitedString, char delimiter, bool escapedEmptyAsZeroLengthString, out int nextFieldStartIndex)
        {
            string result = null;
            var dquoteIsClosed = true;
            for (int i = fieldStartIndex; i <= delimitedString.Length; i++)
            {
                char c = delimiter;
                if (i < delimitedString.Length)
                {
                    c = delimitedString[i];
                }

                if (c == '\"')
                {
                    dquoteIsClosed = !dquoteIsClosed;
                }

                if (dquoteIsClosed && c == delimiter)
                {
                    var fieldLength = i - fieldStartIndex;

                    if (fieldLength == 0)
                    {
                        nextFieldStartIndex = i + 1;
                        return null;
                    }

                    result = delimitedString.Substring(fieldStartIndex, fieldLength);

                    if (String.IsNullOrWhiteSpace(result))
                    {
                        nextFieldStartIndex = i + 1;
                        return null;
                    }

                    result = QuatedToUnquatedText(result);
                    if (String.IsNullOrEmpty(result) && !escapedEmptyAsZeroLengthString)
                    {
                        result = null;
                    }

                    nextFieldStartIndex = i + 1;
                    return result;
                }
            }

            nextFieldStartIndex = delimitedString.Length + 1;
            return result;
        }

        private static string QuatedToUnquatedText(string quatedText)
        {
            var result = String.Copy(quatedText);
            if (!"\"\"".Equals(result))
            {
                result = quatedText.Replace("\"\"", "\"");
            }

            var startIndex = 0;
            for (int i = 0; i < result.Length; i++)
            {
                var c = result[i];

                if (Char.IsWhiteSpace(c))
                {
                    continue;
                }

                if (c == '\"')
                {
                    startIndex = i + 1;
                    break;
                }
                else
                {
                    break;
                }
            }

            var endIndex = quatedText.Length - 1;
            for (int i = result.Length - 1; i >= 0; i--)
            {
                var c = result[i];

                if (Char.IsWhiteSpace(c))
                {
                    continue;
                }

                if (c == '\"')
                {
                    endIndex = i - 1;
                    break;
                }
            }

            result = result.Substring(startIndex, endIndex - startIndex + 1);

            return result;
        }
    }
}
